//arch.cpp

#include "arch.h"
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

CFileLinkerTree::~CFileLinkerTree()
{
	for(unsigned int i=0; i<pointer.size(); ++i)
		delete pointer[i];
}
void CFileLinkerTree::add(CFileLinkerTree* _daddy, std::string _name)
{
	pointer.push_back(new CFileLinkerTree(_daddy, _name,lvl+1));	
}
CFileLinkerTree* CFileLinkerTree::get(int index)
{
	if(pointer.size()!=0)
		return pointer[index];
	return 0;
}

Arch::Arch()
{
	compr=NULL;
}
Arch::~Arch()
{
	//delete compr;
	compr=NULL;
}
int Arch::getFileType(const std::string& fileIn)
{
	DIR *dir=opendir(fileIn.c_str());
	if(dir!=NULL)
	{
		closedir(dir);
		return 2;
	}
	else
	{
		FILE *file=fopen(fileIn.c_str(), "rb");
		if(file)
		{
			fclose(file);
			return 1;
			}
	}
	return 0;
}
int Arch::decompress(std::string fileIn, std::string fileOut, int type, int _smartReset) 
{
	smartReset=_smartReset;
	resetMask.clear();
	fileType=getFileType(fileOut);
	
	filesList.clear();
	if(fileType==0 || fileType==1)
	{
		filesList.push_back(fileOut.c_str());
	}
	FILE* inputFile=fopen(fileIn.c_str(), "rb");

	if(inputFile==NULL)
		return 0;	

	if(type=='l')
		compr = new LZW();
	else if(type=='h')
		compr = new Huffman();
	else
	{
		compr = NULL;
		return 0;
	}
	
	if(!readHeader(inputFile,fileOut))
		return 0;	

	FILE* outputFile;
	int index=0;
	for (std::list<std::string>::const_iterator i = filesList.begin(); i != filesList.end(); ++i, ++index)
	{	
		outputFile=fopen((*i).c_str(), "wb");
		//^TODOсперва проверять если ли такая папка, потом создавать её, ато файл на создасться
		if(outputFile==NULL)
			return 0;
		compr->decompress(inputFile,outputFile);
		if(smartReset==2)
		{
			if(resetMask[index]==1)
				compr->clear();
		}	 
		else
		{
			if(smartReset==1)
				compr->clear();	
		}
		fclose(outputFile);
	}	
	fclose(inputFile);


	delete compr;//!!!!!!!!!!!!!!!!!^ в какуюнибудь функцию
	return 1;
}
int Arch::readHeader(FILE* inputFile, std::string dirName)
{
	int size=0;
	char buffer[1024]={0};
	if(fread(&size, 1, 1, inputFile)==0)
		return 0;
	if(smartReset==2)
	{
		BitStream bitFile;
		if((bitFile.open(inputFile, 'r'))==0)
			return 0;	
		for(int i=0; i<size; ++i)
			resetMask.push_back(bitFile.get(1));
		for(int i=0; i<size%8; ++i)
			bitFile.get(1);	
	}
	std::string temp;
	if(dirName.c_str()[dirName.size()-1]!='/')
		dirName+='/';
	for (int i=0; i<size; ++i)
	{
		temp="";
		if(fscanf(inputFile,"%s",buffer)==0)
			return 0;		
		if(fileType==2)
		{
			temp=dirName;
			temp+=buffer;		
			filesList.push_back(temp);
		}
	}
	if(fread(&size, 1, 1, inputFile)==0)
		return 0;
	return 1;
}
std::vector<CFileLinkerTree*> Arch::resortList(std::vector<CFileLinkerTree*>& vec)
{
	std::vector<CFileLinkerTree*> newvec;
	while(vec.size()>0)
	{
		int max=0;
		for(unsigned int i=0; i<vec.size(); ++i)
		{
			if(vec[i]->getLvl()>max)
				max=i;
		}
		int depth=vec[max]->getLvl();
		unsigned int index=0;
		for(index=max; index>0; --index)
		{
			if(vec[index]->getLvl()+MAX_DEPTH>depth)
			{	
				newvec.push_back(vec[index]);
				vec.erase(vec.begin()+index);
			}	
			else
				break;
		}
		newvec.push_back(vec[index]);
		vec.erase(vec.begin()+index);
		
		if(index+1<vec.size())
		{
			for(; index<vec.size();)
			{
				if(vec[index]->getLvl()+MAX_DEPTH>depth)
				{
					newvec.push_back(vec[index]);
					vec.erase(vec.begin()+index);
				}
				else
					break;
			}
		}
		newvec[newvec.size()-1]->lastInResetBlock=1;
	}
	return newvec;
}
std::list<std::string> Arch::prepareList(std::vector<CFileLinkerTree*> &vec)
{
		//creating new list;
		std::list<std::string> newfilesList;
		std::string str;
		int resetMaskIndex=-1;
		for(unsigned int i=0; i<vec.size(); ++i)
		{
			str=vec[i]->getName();
			str+=".h";
			if(fileExists(str))
			{
				newfilesList.push_back(str);
				++resetMaskIndex;
			}
			str=vec[i]->getName();
			str+=".cpp";
			if(fileExists(str))
			{
				newfilesList.push_back(str);
				++resetMaskIndex;
			}
			if(vec[i]->lastInResetBlock==1)
				resetMask[resetMaskIndex]=1;						
		}
		//deleting all cpp and h files from old
		for (std::list<std::string>::iterator i = filesList.begin(); i != filesList.end(); ++i)
		{
			/*str=basename((*i).c_str());
			if(str.size()>3)
			if(str.substr(str.size()-2,2)==".h")
			{
				i=filesList.erase(i);
				--i;
			}
			if(str.substr(str.size()-4,4)==".cpp")
			{
				i=filesList.erase(i);
				--i;
			}*/
			str=basename((*i).c_str());
			if(str.size()<4)
				continue;
			for (std::list<std::string>::iterator j = newfilesList.begin(); j!= newfilesList.end(); ++j)
			{
				if((*i)==(*j))
				{
					i=filesList.erase(i);
					--i;
				}
			}			
		}
		// joining new and old list;
		for (std::list<std::string>::iterator i = filesList.begin(); i != filesList.end(); ++i)
			newfilesList.push_back((*i));	
		return newfilesList;		
}
int Arch::perebor(std::vector<std::vector<std::string> > & vec)
{
	std::vector<std::string> temp;
	for (std::list<std::string>::const_iterator i = filesList.begin(); i != filesList.end(); ++i)
		temp.push_back((*i));	
	std::string str;
	printf("\nsize-%d",temp.size());
	for(unsigned int i=0; i<temp.size(); ++i)
	{		
		for(unsigned int j=0; j<temp.size()-1; ++j)
		{		
			str=temp[j];
			temp[j]=temp[j+1];
			temp[j+1]=str;
			vec.push_back(temp);
		}
	}
	return 1;
}
int Arch::compress(std::string fileIn, std::string fileOut, char type, int _smartReset) 
{
	smartReset=_smartReset;
	resetMask.clear();
	fileType=getFileType(fileIn);

	filesList.clear();
	if(fileType==0)
		return 0;	
	else if(fileType==1)
	{
		filesList.push_back(fileIn.c_str());
	}
	else if(fileType==2)
	{
		getDirFiles(filesList, fileIn);
	}
	
/*std::vector<std::vector<std::string> > fl;
perebor(fl);	 //TODO delete	
std::string truname=fileOut;
for(unsigned d=0; d<fl.size(); ++d)
{
printf("\nvariand %d from %d",d,fl.size());
std::string name=truname.substr(0,truname.size()-4);
int tempy=d;
for(int i=3;i>=0;--i)
{
	int pol=1;
	for(int t=0; t<i; ++t)
		pol*=10;
	if(i>0)
	{
		tempy=d/pol;
	}
	else
		tempy=d;
	name+=(char)(48+(tempy%10));
}	
name+=".txt";
fileOut=name;
filesList.clear();
for(unsigned int k=0; k<fl[d].size(); ++k)
	filesList.push_back(fl[d][k]);
printf(" {%s}",basename(name.c_str()));
*/
	FILE* outputFile=fopen(fileOut.c_str(), "wb");
	if(outputFile==NULL)
		return 0;		
	
	if(type=='l')
		compr = new LZW;
	else if(type=='h')
		compr = new Huffman;
	else
		return 0;

	if(fileType==2 && smartReset==2)
	{
		cFLT=NULL;
		resortFilesList(fileIn);
		for(unsigned int i=0; i<filesList.size(); ++i)
			resetMask.push_back(0);                     //я ничего еще нигде не сбрасываю
		std::vector<CFileLinkerTree*> vec;
		treeToVector(cFLT, vec);
		vec=resortList(vec);
		filesList=prepareList(vec);
		delete cFLT;
	}
	if(!writeHeader(outputFile,fileIn))
		return 0;
	FILE* inputFile;
	int index=0;
	for (std::list<std::string>::const_iterator i = filesList.begin(); i != filesList.end(); ++i, ++index)
	{
		inputFile=fopen((*i).c_str(), "rb");
		if(inputFile==NULL)
			return 0;
		compr->compress(inputFile,outputFile);
		if(smartReset==2)
		{
			if(resetMask[index]==1)
				compr->clear();
		}	 
		else
		{
			if(smartReset==1)
				compr->clear();	
		}
		fclose(inputFile);
	}	
	fclose(outputFile);

	delete compr;//!!!!!!!!!!!!!!!!!^ в какуюнибудь функцию
//}
	return 1;
}
int Arch::writeHeader(FILE* outputFile, std::string dirName)
{
	int size=filesList.size();
	if(fwrite(&size, 1, 1, outputFile)==0)
		return 0;
	if(smartReset==2)
	{
		BitStream bitFile;
		if((bitFile.open(outputFile, 'w'))==0)
			return 0;	
		for(int i=0; i<size; ++i)
			bitFile.put(resetMask[i],1);
		for(int i=0; i<size%8; ++i)
			bitFile.put(0,1);		
	}
	std::string temp;
	for (std::list<std::string>::const_iterator i = filesList.begin(); i != filesList.end(); ++i)
	{
		temp=(*i);		
		if(fileType==1)
			temp=basename(temp.c_str());
		else if(fileType==2)
			temp.erase(0,dirName.size());
		temp+="\n";		
		if(fwrite(temp.c_str(), 1, temp.size(), outputFile)==0)
			return 0;		
	}
	return 1;	
}
int Arch::resortFilesList(std::string dirWay)
{
	//gcc -MM ../tests/test3/testFolder/*.cpp > ../tests/test3/testFolder/__tempNecessaryFile.tmp
	ls.clear();
	cFileLinks.clear(); //TODO засунуть это в структуру
	cFileLinksName.clear();
	std::vector<std::string> linkersSlot;
	std::string arg1=dirWay, arg2=dirWay, str;
	std::list<std::string>::const_iterator i = filesList.begin();
	//str=basename((*i).c_str());
	//arg1+=str;
	//arg1=canonicalize_file_name(arg1.c_str());
	//arg1.erase(arg1.size()-str.size(),str.size());
	arg1+="*.cpp";
	arg2+="linkFile.lf"; 
	//printf("%s", arg1.c_str()); getchar();
	//execlp("g++", "g++", "-MM ", arg1.c_str(), ">", arg2.c_str(),  NULL);
	//getchar();
	char buffer[1024];
	FILE* file = fopen(arg2.c_str(),"rb");
	while(fscanf(file,"%s",buffer)!=EOF)
	{
		str=buffer;
		
		if(str.size()<4)
			continue;
		if(str.substr(str.size()-3,3)==".o:")
		{
			linkersSlot.clear();
			cFileLinks.push_back(linkersSlot);
			cFileLinksName.push_back(str.substr(0,str.size()-3));
		}
		if(str.substr(str.size()-4,4)==".cpp")
		{
			cFileLinks[cFileLinks.size()-1].push_back(str.substr(0,str.size()-4));
			ls.insert(str.substr(0,str.size()-4));	
			
		}
		if(str.substr(str.size()-2,2)==".h")
		{
			cFileLinks[cFileLinks.size()-1].push_back(str.substr(0,str.size()-2));
			ls.insert(str.substr(0,str.size()-2));
		}
	}
	for(unsigned int i=0; i<cFileLinksName.size(); ++i)
	{
		if(cFileLinksName[i]=="main")
		{
			linkersSlot=cFileLinks[i];
			cFileLinks[i]=cFileLinks[0];
			cFileLinks[0]=linkersSlot;
			str=cFileLinksName[i];
			cFileLinksName[i]=cFileLinksName[0];
			cFileLinksName[0]=str;
		}
	}
/*	printf("\n\n");
	for(unsigned int i=0; i<cFileLinks.size(); ++i)
	{			
		printf("\n----%s",cFileLinksName[i].c_str());
		for(unsigned int j = 0; j<cFileLinks[i].size(); ++j)
		{
				printf("\n-%s",cFileLinks[i][j].c_str());
		}	
	}	
	
	
	for(std::set<std::string>::const_iterator j = ls.begin(); j != ls.end(); ++j)
			printf("\n!!%s",(*j).c_str());
	printf("\n\n");*/

	cFLT=new CFileLinkerTree(NULL, "$$$$$",0);
	walkLink(0, cFLT);
	/*for(std::set<std::string>::const_iterator i = ls.begin(); i != ls.end(); ++i)
	{
		printf("\n%s",(*i).c_str());
	} */
	fclose(file);
	//unlink(arg2);
	return 1;
}
void Arch::treeToVector(CFileLinkerTree* p, std::vector<CFileLinkerTree*> &vec)
{
	for(int i=0; i<p->size(); ++i)
	{
		vec.push_back(p->get(i));
		treeToVector(p->get(i),vec);
	}
}
int Arch::walkLink(int i, CFileLinkerTree* p)
{
	std::string temp;
	for(unsigned int j = 0; j<cFileLinks[i].size(); ++j)
	{
		for(std::set<std::string>::const_iterator it = ls.begin(); it != ls.end(); ++it)
		{
			if((*it)==cFileLinks[i][j])
			{	
				ls.erase(it);
				p->add(p, cFileLinks[i][j]);
				for(unsigned int u=0; u<cFileLinksName.size(); ++u)
				{
					temp=basename(cFileLinks[i][j].c_str());
					if(cFileLinksName[u]==temp)
					{
						walkLink(u,p->get(p->size()-1));
						break;
					}
				}
				break;
			}
		}		
	}
	return 1;
}
